"""
设置相关API路由
"""
from typing import Optional
from fastapi import APIRouter, HTTPException, Request
from pydantic import BaseModel
import json
import ipaddress
from datetime import datetime

from app.core.redis_client import redis_manager
from app.utils.logger import get_logger
from app.config import get_settings

router = APIRouter()
logger = get_logger(__name__)
settings = get_settings()

# IP白名单Redis键
IP_WHITELIST_KEY = "settings:ip_whitelist"


def get_client_ip(request: Request) -> str:
    """获取客户端真实IP地址"""
    # 优先从X-Forwarded-For头获取
    forwarded_for = request.headers.get("X-Forwarded-For")
    if forwarded_for:
        # X-Forwarded-For可能包含多个IP，取第一个
        return forwarded_for.split(",")[0].strip()

    # 从X-Real-IP头获取
    real_ip = request.headers.get("X-Real-IP")
    if real_ip:
        return real_ip.strip()

    # 最后使用客户端IP
    return request.client.host if request.client else "unknown"


async def check_ip_whitelist(ip: str) -> bool:
    """检查IP是否在白名单中"""
    try:
        # 获取白名单数据
        whitelist_data = await redis_manager.get_json(IP_WHITELIST_KEY)
        if not whitelist_data:
            # 如果没有白名单，默认允许localhost和内网IP
            return is_local_or_private_ip(ip)

        whitelist = whitelist_data.get("ips", [])

        # 检查精确匹配
        for entry in whitelist:
            allowed_ip = entry.get("ip", "")
            if ip == allowed_ip:
                return True

            # 检查CIDR网段匹配
            try:
                if "/" in allowed_ip:
                    network = ipaddress.ip_network(allowed_ip, strict=False)
                    if ipaddress.ip_address(ip) in network:
                        return True
            except (ipaddress.AddressValueError, ValueError):
                continue

        return False

    except Exception as e:
        logger.error(f"检查IP白名单失败: {e}")
        # 出错时默认允许本地IP
        return is_local_or_private_ip(ip)


def is_local_or_private_ip(ip: str) -> bool:
    """检查是否为本地或内网IP"""
    try:
        ip_obj = ipaddress.ip_address(ip)
        return (
            ip_obj.is_loopback or
            ip_obj.is_private or
            ip == "127.0.0.1" or
            ip == "localhost"
        )
    except (ipaddress.AddressValueError, ValueError):
        return ip in ["127.0.0.1", "localhost", "::1"]


async def add_to_ip_whitelist(ip: str, description: str = "") -> bool:
    """添加IP到白名单"""
    try:
        # 验证IP格式
        try:
            if "/" in ip:
                ipaddress.ip_network(ip, strict=False)
            else:
                ipaddress.ip_address(ip)
        except (ipaddress.AddressValueError, ValueError):
            raise ValueError(f"无效的IP地址格式: {ip}")

        # 获取现有白名单
        whitelist_data = await redis_manager.get_json(IP_WHITELIST_KEY) or {"ips": []}

        # 检查是否已存在
        for entry in whitelist_data["ips"]:
            if entry.get("ip") == ip:
                return True  # 已存在

        # 添加新IP
        whitelist_data["ips"].append({
            "ip": ip,
            "description": description,
            "added_at": datetime.now().isoformat()
        })

        # 保存到Redis
        await redis_manager.set_json(IP_WHITELIST_KEY, whitelist_data)
        return True

    except Exception as e:
        logger.error(f"添加IP到白名单失败: {e}")
        return False


async def remove_from_ip_whitelist(ip: str) -> bool:
    """从白名单中移除IP"""
    try:
        # 获取现有白名单
        whitelist_data = await redis_manager.get_json(IP_WHITELIST_KEY)
        if not whitelist_data:
            return True  # 白名单为空，认为移除成功

        # 移除指定IP
        original_count = len(whitelist_data["ips"])
        whitelist_data["ips"] = [
            entry for entry in whitelist_data["ips"]
            if entry.get("ip") != ip
        ]

        # 保存到Redis
        await redis_manager.set_json(IP_WHITELIST_KEY, whitelist_data)

        return len(whitelist_data["ips"]) < original_count

    except Exception as e:
        logger.error(f"从白名单移除IP失败: {e}")
        return False


async def get_ip_whitelist_data() -> list:
    """获取IP白名单数据"""
    try:
        whitelist_data = await redis_manager.get_json(IP_WHITELIST_KEY)
        if not whitelist_data:
            return []
        return whitelist_data.get("ips", [])
    except Exception as e:
        logger.error(f"获取IP白名单数据失败: {e}")
        return []


class KafkaConfigRequest(BaseModel):
    """Kafka配置请求"""
    bootstrap_servers: str
    security_protocol: str = "PLAINTEXT"
    sasl_mechanism: str = "PLAIN"
    sasl_username: Optional[str] = None
    sasl_password: Optional[str] = None
    api_version: Optional[str] = "auto"


class KafkaConfigResponse(BaseModel):
    """Kafka配置响应"""
    bootstrap_servers: str
    security_protocol: str
    sasl_mechanism: str
    sasl_username: Optional[str] = None
    api_version: Optional[str] = "auto"
    # 不返回密码


class KsqlConfigRequest(BaseModel):
    """KSQL配置请求"""
    ksql_server_url: str
    ksql_username: Optional[str] = None
    ksql_password: Optional[str] = None
    ksql_timeout: int = 30000
    ksql_enabled: bool = False


class KsqlConfigResponse(BaseModel):
    """KSQL配置响应"""
    ksql_server_url: str
    ksql_username: Optional[str] = None
    ksql_timeout: int = 30000
    ksql_enabled: bool = False
    # 不返回密码


@router.post("/kafka")
async def save_kafka_config(config: KafkaConfigRequest):
    """保存Kafka配置到Redis"""
    try:
        # 保存到Redis
        config_data = config.model_dump()
        success = await redis_manager.set_json(
            "kafka:config:connection", 
            config_data, 
            ttl=None  # 永久保存
        )
        
        if success:
            logger.info("Kafka配置保存成功")
            return {"success": True, "message": "配置保存成功"}
        else:
            raise HTTPException(status_code=500, detail="配置保存失败")
            
    except Exception as e:
        logger.error(f"保存Kafka配置失败: {e}")
        raise HTTPException(status_code=500, detail=f"保存配置失败: {str(e)}")


@router.get("/kafka", response_model=KafkaConfigResponse)
async def get_kafka_config():
    """从Redis获取Kafka配置"""
    try:
        config_data = await redis_manager.get_json("kafka:config:connection")
        
        if config_data is None:
            # 返回默认配置
            return KafkaConfigResponse(
                bootstrap_servers=settings.kafka.bootstrap_servers,
                security_protocol=settings.kafka.security_protocol,
                sasl_mechanism=settings.kafka.sasl_mechanism,
                sasl_username=settings.kafka.sasl_username,
                api_version="auto"
            )
        
        # 不返回密码字段
        config_data.pop("sasl_password", None)
        return KafkaConfigResponse(**config_data)
        
    except Exception as e:
        logger.error(f"获取Kafka配置失败: {e}")
        raise HTTPException(status_code=500, detail=f"获取配置失败: {str(e)}")


@router.delete("/kafka")
async def delete_kafka_config():
    """删除Kafka配置"""
    try:
        deleted = await redis_manager.delete("kafka:config:connection")
        
        if deleted > 0:
            logger.info("Kafka配置删除成功")
            return {"success": True, "message": "配置删除成功"}
        else:
            return {"success": True, "message": "配置不存在"}
            
    except Exception as e:
        logger.error(f"删除Kafka配置失败: {e}")
        raise HTTPException(status_code=500, detail=f"删除配置失败: {str(e)}")


@router.post("/kafka/test")
async def test_kafka_config(config: KafkaConfigRequest):
    """测试Kafka配置连接（真实调用，带版本回退）"""
    logger.info("=== 开始Kafka配置测试 ===")
    logger.info(f"接收到配置: {config.model_dump()}")

    try:
        # 基本验证
        if not config.bootstrap_servers:
            raise HTTPException(status_code=400, detail="Bootstrap servers不能为空")

        # 延迟导入避免循环依赖
        from app.core.kafka_client import kafka_manager

        # 备份当前配置
        original_config = await redis_manager.get_json("kafka:config:connection")

        try:
            # 将测试配置写入Redis（设置TTL，避免污染长期配置）
            test_config_data = config.model_dump()
            await redis_manager.set_json("kafka:config:connection", test_config_data, ttl=60)

            # 关闭现有客户端，用测试配置重建
            await kafka_manager.close_all()

            # 使用支持回退的智能测试
            result = await kafka_manager.test_connection_with_fallback()
            logger.info(f"Kafka连接测试结果: {result}")
            return result
        finally:
            # 恢复原有配置
            if original_config:
                await redis_manager.set_json("kafka:config:connection", original_config)
            else:
                await redis_manager.delete("kafka:config:connection")
            # 再次重置客户端，恢复到原配置
            await kafka_manager.close_all()

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"测试Kafka连接失败: {e}", exc_info=True)
        return {
            "success": False,
            "message": f"连接测试失败: {str(e)}",
            "brokers": 0,
            "api_version": None
        }


@router.get("/ip-whitelist/check")
async def check_ip_access(request: Request):
    """检查当前IP是否在白名单中"""
    try:
        # 获取客户端IP
        client_ip = get_client_ip(request)

        # 检查IP是否在白名单中
        is_allowed = await check_ip_whitelist(client_ip)

        return {
            "allowed": is_allowed,
            "client_ip": client_ip
        }

    except Exception as e:
        logger.error(f"检查IP白名单失败: {e}")
        return {
            "allowed": False,
            "client_ip": "unknown"
        }


@router.post("/ip-whitelist/add")
async def add_ip_to_whitelist(request: Request, ip_data: dict):
    """添加IP到白名单（需要当前IP已在白名单中）"""
    try:
        # 获取客户端IP
        client_ip = get_client_ip(request)

        # 检查当前IP是否有权限
        if not await check_ip_whitelist(client_ip):
            raise HTTPException(status_code=403, detail="当前IP无权限管理白名单")

        new_ip = ip_data.get("ip")
        description = ip_data.get("description", "")

        if not new_ip:
            raise HTTPException(status_code=400, detail="IP地址不能为空")

        # 添加到白名单
        await add_to_ip_whitelist(new_ip, description)

        logger.info(f"IP {new_ip} 已被 {client_ip} 添加到白名单")
        return {"success": True, "message": f"IP {new_ip} 已添加到白名单"}

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"添加IP到白名单失败: {e}")
        raise HTTPException(status_code=500, detail="添加IP到白名单失败")


@router.delete("/ip-whitelist/{ip}")
async def remove_ip_from_whitelist(request: Request, ip: str):
    """从白名单中移除IP（需要当前IP已在白名单中）"""
    try:
        # 获取客户端IP
        client_ip = get_client_ip(request)

        # 检查当前IP是否有权限
        if not await check_ip_whitelist(client_ip):
            raise HTTPException(status_code=403, detail="当前IP无权限管理白名单")

        # 不允许删除自己的IP
        if ip == client_ip:
            raise HTTPException(status_code=400, detail="不能删除自己的IP")

        # 从白名单中移除
        await remove_from_ip_whitelist(ip)

        logger.info(f"IP {ip} 已被 {client_ip} 从白名单中移除")
        return {"success": True, "message": f"IP {ip} 已从白名单中移除"}

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"从白名单移除IP失败: {e}")
        raise HTTPException(status_code=500, detail="从白名单移除IP失败")


@router.get("/ip-whitelist")
async def get_ip_whitelist(request: Request):
    """获取IP白名单列表（需要当前IP已在白名单中）"""
    try:
        # 获取客户端IP
        client_ip = get_client_ip(request)

        # 检查当前IP是否有权限
        if not await check_ip_whitelist(client_ip):
            raise HTTPException(status_code=403, detail="当前IP无权限查看白名单")

        # 获取白名单
        whitelist = await get_ip_whitelist_data()

        return {
            "success": True,
            "data": whitelist,
            "current_ip": client_ip
        }

    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"获取IP白名单失败: {e}")
        raise HTTPException(status_code=500, detail="获取IP白名单失败")


@router.post("/ksql")
async def save_ksql_config(config: KsqlConfigRequest):
    """保存KSQL配置到Redis"""
    try:
        # 保存到Redis
        config_data = config.model_dump()
        success = await redis_manager.set_json(
            "ksql:config:connection",
            config_data,
            ttl=None  # 永久保存
        )

        if success:
            logger.info("KSQL配置保存成功")
            return {"success": True, "message": "KSQL配置保存成功"}
        else:
            logger.error("KSQL配置保存到Redis失败")
            raise HTTPException(status_code=500, detail="配置保存失败")

    except Exception as e:
        logger.error(f"保存KSQL配置失败: {e}", exc_info=True)
        raise HTTPException(status_code=500, detail=f"保存配置失败: {str(e)}")


@router.get("/ksql", response_model=KsqlConfigResponse)
async def get_ksql_config():
    """从Redis获取KSQL配置"""
    try:
        config_data = await redis_manager.get_json("ksql:config:connection")

        if config_data:
            # 不返回密码
            config_data.pop('ksql_password', None)
            return KsqlConfigResponse(**config_data)
        else:
            # 返回默认配置
            return KsqlConfigResponse(
                ksql_server_url="http://localhost:8088",  # KSQL没有在.env中配置，保持默认
                ksql_username=None,
                ksql_timeout=30000,
                ksql_enabled=False
            )

    except Exception as e:
        logger.error(f"获取KSQL配置失败: {e}", exc_info=True)
        # 返回默认配置
        return KsqlConfigResponse(
            ksql_server_url="http://localhost:8088",  # KSQL没有在.env中配置，保持默认
            ksql_username=None,
            ksql_timeout=30000,
            ksql_enabled=False
        )


@router.post("/ksql/test")
async def test_ksql_connection(config: KsqlConfigRequest):
    """测试KSQL连接"""
    try:
        import aiohttp
        import asyncio

        # 构建认证头
        headers = {'Content-Type': 'application/json'}
        if config.ksql_username and config.ksql_password:
            import base64
            credentials = base64.b64encode(f"{config.ksql_username}:{config.ksql_password}".encode()).decode()
            headers['Authorization'] = f'Basic {credentials}'

        # 测试连接 - 使用官方API端点
        timeout = aiohttp.ClientTimeout(total=config.ksql_timeout / 1000)
        async with aiohttp.ClientSession(timeout=timeout) as session:
            # 首先测试 /info 端点
            async with session.get(f"{config.ksql_server_url}/info", headers=headers) as response:
                if response.status == 200:
                    info = await response.json()
                    version = info.get('KsqlServerInfo', {}).get('version', '未知')
                    server_id = info.get('KsqlServerInfo', {}).get('serverID', '未知')
                    kafka_cluster_id = info.get('KsqlServerInfo', {}).get('kafkaClusterId', '未知')

                    return {
                        "success": True,
                        "message": f"KSQL连接成功 - 版本: {version}",
                        "version": version,
                        "server_id": server_id,
                        "kafka_cluster_id": kafka_cluster_id
                    }
                else:
                    return {
                        "success": False,
                        "message": f"KSQL连接失败: {response.status} {response.reason}"
                    }

    except asyncio.TimeoutError:
        return {
            "success": False,
            "message": "KSQL连接超时"
        }
    except Exception as e:
        logger.error(f"测试KSQL连接失败: {e}", exc_info=True)
        return {
            "success": False,
            "message": f"KSQL连接失败: {str(e)}"
        }
